<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>G6 自定义节点：背景图 + 居中文本</title>
    <style>
      html,
      body {
        margin: 0;
        height: 100%;
        overflow: hidden;
      }
      #container {
        width: 100%;
        height: 100%;
        background-color: #0b1c32;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
    <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.8.23/dist/g6.min.js"></script>
    <script>
      const width = window.innerWidth
      const height = window.innerHeight

      // 注册节点
      G6.registerNode("custom-image-node", {
        draw(cfg, group) {
          const size = cfg.size || [100, 100]
          const [w, h] = size

          // 背景图片
          const imageShape = group.addShape("image", {
            attrs: {
              x: -w / 2,
              y: -h / 2,
              width: w,
              height: h,
              img: cfg.img // 替换为你的图
            },
            name: "node-image",
            draggable: true
          })

          // 文本标签
          if (cfg.label) {
            group.addShape("text", {
              attrs: {
                text: cfg.label,
                x: 0,
                y: h / 2 + 14,
                fontSize: 14,
                fill: "#ffffff",
                textAlign: "center",
                textBaseline: "middle"
              },
              name: "node-label"
            })
          }

          return imageShape
        },

        // 可选：响应状态变化
        setState(name, value, item) {
          const group = item.getContainer()
          const image = group.find((ele) => ele.get("name") === "node-image")

          if (name === "hover") {
            image && image.attr("shadowColor", value ? "#ffffff" : null)
            image && image.attr("shadowBlur", value ? 20 : 0)
          }

          if (name === "selected") {
            image && image.attr("stroke", value ? "#00FFFF" : null)
            image && image.attr("lineWidth", value ? 2 : 0)
          }
        }
      })

      // 初始化图
      const graph = new G6.Graph({
        container: "container",
        width,
        height,
        modes: {
          default: ["drag-node", "zoom-canvas", "drag-canvas"]
        },
        defaultNode: {
          type: "custom-image-node",
          size: [100, 100]
        }
      })

      const nodes = [
        // 根节点
        { id: "root", label: "中心节点" },

        // 左侧分支：第1层
        { id: "left1", label: "左1", parent: "root", direction: "left" },
        { id: "left2", label: "左2", parent: "root", direction: "left" },

        // 左侧分支：第2层
        { id: "left1-1", label: "左1-1", parent: "left1", direction: "left" },
        { id: "left2-1", label: "左2-1", parent: "left2", direction: "left" },
        { id: "left2-2", label: "左2-2", parent: "left2", direction: "left" },

        // 左侧分支：第3层
        {
          id: "left2-1-1",
          label: "左2-1-1",
          parent: "left2-1",
          direction: "left"
        },
        {
          id: "left2-1-2",
          label: "左2-1-2",
          parent: "left2-1",
          direction: "left"
        },

        // 右侧分支：第1层
        { id: "right1", label: "右1", parent: "root", direction: "right" },
        { id: "right2", label: "右2", parent: "root", direction: "right" },

        // 右侧分支：第2层
        {
          id: "right1-1",
          label: "右1-1",
          parent: "right1",
          direction: "right"
        },
        {
          id: "right2-1",
          label: "右2-1",
          parent: "right2",
          direction: "right"
        },

        // 右侧分支：第3层
        {
          id: "right2-1-1",
          label: "右2-1-1",
          parent: "right2-1",
          direction: "right"
        }
      ]

      function layoutRecursiveLeftRight(
        nodes,
        rootId,
        centerX,
        centerY,
        spacingX = 180,
        spacingY = 80
      ) {
        const idMap = {}
        nodes.forEach((n) => (idMap[n.id] = n))

        const root = idMap[rootId]
        root.x = centerX
        root.y = centerY

        const placed = new Set([rootId])

        function layoutChildren(
          parentId,
          direction,
          depth = 1,
          baseY = centerY
        ) {
          const children = nodes.filter(
            (n) => n.parent === parentId && n.direction === direction
          )
          const total = children.length
          const offsetStartY = baseY - ((total - 1) * spacingY) / 2

          children.forEach((child, i) => {
            const x =
              direction === "left"
                ? centerX - spacingX * depth
                : centerX + spacingX * depth
            const y = offsetStartY + i * spacingY

            child.x = x
            child.y = y
            placed.add(child.id)

            // 递归布局子节点
            layoutChildren(child.id, direction, depth + 1, y)
          })
        }

        layoutChildren(rootId, "left")
        layoutChildren(rootId, "right")

        // 返回重新排列后的节点数组（保持原顺序）
        return nodes.map((n) => idMap[n.id])
      }

      const newNodes = layoutRecursiveLeftRight(
        nodes,
        "root",
        width / 2,
        height / 2
      )

      // 加载数据
      graph.data({
        nodes: newNodes
      })

      graph.render()

      // 自适应窗口大小
      window.addEventListener("resize", () => {
        if (!graph || graph.get("destroyed")) return
        const newW = window.innerWidth
        const newH = window.innerHeight
        graph.changeSize(newW, newH)
        const node = graph.findById("root")
        graph.updateItem(node, { x: newW / 2, y: newH / 2 })
      })
    </script>
  </body>
</html>
